

#include "QDictionary.h"
#include "QArray.h"
#include "QMetaTypeEx.h"

#include <QtXml/QtXml>


QDictionary::QDictionary()
	: mKeyList()
{
    qRegisterMetaType<QDictionary>();
}

QDictionary::QDictionary(const QDictionary &other)
	: QVariantHash(other)
	, mKeyList(other.mKeyList)
{

}

QDictionary::~QDictionary()
{
	clear();
}

QDictionary &QDictionary::operator=(const QDictionary &other)
{
	QVariantHash::operator=(other);
	mKeyList = other.mKeyList;
	return *this;
}

bool QDictionary::load(const QString &filename)
{
	QFile file(filename);

	if (!file.open((QIODevice::ReadOnly|QIODevice::Text)))
	{
		return false;
	}

	if (!unarchive(&file))
	{
		file.close();
		return false;
	}

	file.close();

	return true;
}

bool QDictionary::save(const QString &filename)
{
	QFile file(filename);

	if (!file.open(QIODevice::WriteOnly|QIODevice::Text))
	{
		return false;
	}

	if (!archive(&file))
	{
		file.close();
		return false;
	}

	file.close();

	return true;
}

bool QDictionary::unarchive(QIODevice *device)
{
	QDomDocument doc("dictionary");

	if (!doc.setContent(device))
	{
		return false;
	}

	QDomElement root = doc.documentElement();

	QDomNode n = root.firstChild();

	while (!n.isNull())
	{
		QDomElement e = n.toElement();
		if (!e.isNull())
		{
			QString tag = e.tagName();
			QString value = e.text();

			if (tag == "dict")
			{
				unserialize(e);
			}
		}

		n = n.nextSibling();
	}

	return true;
}

bool QDictionary::archive(QIODevice *device)
{
	QDomImplementation implementation;
	QDomDocumentType docType = implementation.createDocumentType(
		"plist", "-//Roc Studio//DTD PLIST 1.0//EN", "http://www.roc-studio.com.cn");

	QDomDocument doc(docType);

	QDomProcessingInstruction instruction = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"");
	doc.appendChild(instruction);

	QDomElement root = doc.createElement("plist");
	root.setAttribute("version", "1.0");
	doc.appendChild(root);

	QDomElement e = doc.createElement("dict");
	serialize(doc, e);
	root.appendChild(e);

	QTextStream out(device);
	out.setCodec("UTF-8");
	doc.save(out, 4);

	return true;
}

QDictionary::const_order_iterator QDictionary::orderBegin() const
{
	return mKeyList.begin();
}

QDictionary::const_order_iterator QDictionary::orderEnd() const
{
	return mKeyList.end();
}

QVariant QDictionary::orderValue(const_order_iterator itr) const
{
	const QString &key = *itr;
	return value(key);
}

QDictionary::iterator QDictionary::insert(const QString &key, const QVariant &value)
{
    QDictionary::iterator itr = find(key);
    if (itr == end())
    {
        mKeyList.push_back(key);
        return QVariantHash::insert(key, value);
    }

    itr.value() = value;
    return itr;
}

QDictionary::iterator QDictionary::erase(iterator pos)
{
	const QString &key = pos.key();
	QStringList::iterator itr = mKeyList.begin();
	bool found = false;

	while (itr != mKeyList.end())
	{
		if (*itr == key)
		{
			found = true;
			break;
		}

		++itr;
	}

	if (found)
	{
		mKeyList.erase(itr);
		return QVariantHash::erase(pos);
	}
	
	return pos;
}

void QDictionary::clear()
{
	mKeyList.clear();
	QVariantHash::clear();
}

void QDictionary::display() const
{
	qDebug()<<"<dict>\n";

	const_order_iterator itr = orderBegin();

	while (itr != orderEnd())
	{
		const QString &key = *itr;
		const QVariant &var = value(key);
		int type = var.userType();

		qDebug()<<"<key>"<<key<<"</key>\n";

        if (type == QMetaTypeEx::Dictionary)
		{
			QDictionary dict = var.value<QDictionary>();
			dict.display();
		}
        else if (type == QMetaTypeEx::Array)
		{
			QArray arr = var.value<QArray>();
            int a = arr.size();
            qDebug()<<"size"<<a<<"\n";
			arr.display();
		}
		else if (type == QMetaType::Int)
		{
			qDebug()<<"<integer>"<<var.toInt()<<"</integer>\n";
		}
		else if (type == QMetaType::Bool)
		{
			qDebug()<<"<bool>"<<var.toBool()<<"</bool>\n";
		}
		else if (type == QMetaType::QString)
		{
			qDebug()<<"<string>"<<var.toString()<<"</string>\n";
		}
		else if (type == QMetaType::Float)
		{
			qDebug()<<"<real>"<<var.toFloat()<<"</real>\n";
		}
		else
		{
			qDebug()<<"Unsupported type !!!\n";
			Q_ASSERT(0);
		}

		++itr;
	}

	qDebug()<<"</dict>\n";
}

bool QDictionary::serialize(QDomDocument &doc, QDomElement &root)
{
	const_order_iterator itr = orderBegin();

	while (itr != orderEnd())
	{
		const QString &key = *itr;
		const QVariant &var = value(key);

		QDomElement e = doc.createElement("key");
		QDomText t = doc.createTextNode(key);
		e.appendChild(t);
		root.appendChild(e);

		int type = var.userType();

        if (type == QMetaTypeEx::Dictionary)
		{
			e = doc.createElement("dict");
			QDictionary dict = var.value<QDictionary>();
			dict.serialize(doc, e);
			root.appendChild(e);
		}
        else if (type == QMetaTypeEx::Array)
		{
			e = doc.createElement("array");
			QArray arr = var.value<QArray>();
			arr.serialize(doc, e);
			root.appendChild(e);
		}
		else 
		{			
			if (type == QMetaType::Int)
			{
				int val = var.toInt();
				QString str;
				str.setNum(val);
				e = doc.createElement("integer");
				t = doc.createTextNode(str);
			}
			else if (type == QMetaType::Bool)
			{
				bool val = var.toBool();
				QString str;

				if (val)
				{
					str = "true";
				}
				else
				{
					str = "false";
				}
				
				e = doc.createElement("bool");
				t = doc.createTextNode(str);
			}
			else if (type == QMetaType::Float)
			{
				float val = var.toFloat();
				QString str;
				str.setNum(val);
				e = doc.createElement("real");
				t = doc.createTextNode(str);
			}
			else if (type == QMetaType::QString)
			{
				QString val = var.toString();
				e = doc.createElement("string");
				t = doc.createTextNode(val);
			}
			else
			{
				qDebug()<<"Unsupported type !!!\n";
				Q_ASSERT(0);
			}

			e.appendChild(t);
			root.appendChild(e);
		}

		++itr;
	}

	return true;
}

bool QDictionary::unserialize(QDomElement &docElem)
{
	QString key;
	QDomNode n = docElem.firstChild();

	while (!n.isNull())
	{		
		QDomElement e = n.toElement();

		if (!e.isNull())
		{
			QString tag = e.tagName();
			QString val = e.text();

			if (tag == "key")
			{
				key = val;
			}
			else if (tag == "dict")
			{
				QDictionary dict;
				dict.unserialize(e);
				QVariant var;
				var.setValue(dict);
				insert(key, var);
			}
			else if (tag == "array")
			{
				QArray arr;
				arr.unserialize(e);
				QVariant var;
				var.setValue(arr);
				insert(key, var);
			}
			else
			{
				if (tag == "integer")
				{
					int v = val.toInt();
					QVariant var(v);
					insert(key, var);
				}
				else if (tag == "real")
				{
					float v = val.toFloat();
					QVariant var(v);
					insert(key, var);
				}
				else if (tag == "bool")
				{
					bool v;

					if (val == "true")
					{
						v = true;
					}
					else
					{
						v = false;
					}

					QVariant var(v);
					insert(key, var);
				}
				else if (tag == "string")
				{
					QVariant var(val);
					insert(key, var);
				}
				else
				{
					qDebug()<<"Unsupported type !!!\n";
					Q_ASSERT(0);
				}
			}
		}

		n = n.nextSibling();
	}

	return true;
}

